﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DTO;
using System.Xml;
using System.Globalization;
using System.Data;
using System.Drawing;
using System.IO;

namespace DAO
{
    public class XMLHelper
    {
        public static int GetAttributeAsHex(XmlNode node, string attName)
        {
            int outInt = 0;
            int.TryParse(((XmlElement)node).GetAttribute(attName)
                    , NumberStyles.AllowHexSpecifier, CultureInfo.InvariantCulture.NumberFormat, out outInt);
            return outInt;
        }
        public static int GetAttributeAsInt(XmlNode node, string attName)
        {
            int outInt = 0;
            int.TryParse(((XmlElement)node).GetAttribute(attName)
                    , out outInt);
            return outInt;
        }

        internal static float GetAttributeAsFloat(XmlNode node, string attName)
        {
            float outFloat = 0;
            float.TryParse(((XmlElement)node).GetAttribute(attName),out outFloat);
            return outFloat;
        }
    }
    public class SpriteDAO
    {
        private static DataTable s_datatableModules;
        private static void InitDataTableModules()
        {
            s_datatableModules = new DataTable("Modules");

            // create columns for the DataTable
            DataColumn no = new DataColumn("No", typeof(System.Int32));

            s_datatableModules.Columns.Add(no);
            // create another column
            DataColumn id = new DataColumn("Id", typeof(string));
            s_datatableModules.Columns.Add(id);
            //DataColumn status = new DataColumn("Status", typeof(string));
            //s_datatableModules.Columns.Add(status);
            // create one more column
            DataColumn x = new DataColumn("X", typeof(System.Int32));
            s_datatableModules.Columns.Add(x);
            DataColumn y = new DataColumn("Y", typeof(System.Int32));
            s_datatableModules.Columns.Add(y);
            DataColumn w = new DataColumn("W", typeof(System.Int32));
            s_datatableModules.Columns.Add(w);
            DataColumn h = new DataColumn("H", typeof(System.Int32));
            s_datatableModules.Columns.Add(h);
            DataColumn desc = new DataColumn("Desc", typeof(string));
            s_datatableModules.Columns.Add(desc);


            // create primary key on this field
            DataColumn[] pK = new DataColumn[1];
            pK[0] = id;
            s_datatableModules.PrimaryKey = pK;
        }
        public static DataTable GetTableModule()
        {
            if (s_datatableModules == null)
            {
                InitDataTableModules();
            }
            //else
            //{
            //    s_datatableModule.Clear();
            //}
            return s_datatableModules;
        }

        private static DataTable s_datatableFrames;
        private static void InitDataTableFrames()
        {
            s_datatableFrames = new DataTable("Frames");

            // create columns for the DataTable
            DataColumn no = new DataColumn("No", typeof(System.Int32));
            s_datatableFrames.Columns.Add(no);
            //DataColumn status = new DataColumn("Status", typeof(string));
            //s_datatableFrames.Columns.Add(status);
            // create another column
            DataColumn id = new DataColumn("Id", typeof(string));
            s_datatableFrames.Columns.Add(id);
            // create one more column
            DataColumn fmodules = new DataColumn("FModules", typeof(System.Int32));
            s_datatableFrames.Columns.Add(fmodules);     
            DataColumn desc = new DataColumn("Desc", typeof(string));
            s_datatableFrames.Columns.Add(desc);


            // create primary key on this field
            DataColumn[] pK = new DataColumn[1];
            pK[0] = id;
            s_datatableFrames.PrimaryKey = pK;
        }
        public static DataTable GetTableFrames()
        {
            if (s_datatableFrames == null)
            {
                InitDataTableFrames();
            }
            return s_datatableFrames;
        }

        private static DataTable s_datatableFModules;
        private static void InitDataTableFModules()
        {
            s_datatableFModules = new DataTable("FModules");

            // create columns for the DataTable
            DataColumn no = new DataColumn("No", typeof(System.Int32));
            s_datatableFModules.Columns.Add(no);
            //DataColumn status = new DataColumn("Status", typeof(string));
            //s_datatableFModules.Columns.Add(status);
            // create another column
            DataColumn id = new DataColumn("MId", typeof(string));
            s_datatableFModules.Columns.Add(id);
            DataColumn ox = new DataColumn("Ox", typeof(System.Int32));
            s_datatableFModules.Columns.Add(ox);
            DataColumn oy = new DataColumn("Oy", typeof(System.Int32));
            s_datatableFModules.Columns.Add(oy);
            DataColumn flag = new DataColumn("Flag", typeof(string));
            s_datatableFModules.Columns.Add(flag);

        }
        public static DataTable GetTableFModules()
        {
            if (s_datatableFModules == null)
            {
                InitDataTableFModules();
            }
            return s_datatableFModules;
        }

        private static DataTable s_datatableAnimations;
        private static void InitDataTableAnimations()
        {
            s_datatableAnimations = new DataTable("Frames");

            // create columns for the DataTable
            DataColumn no = new DataColumn("No", typeof(System.Int32));
            s_datatableAnimations.Columns.Add(no);
            //DataColumn status = new DataColumn("Status", typeof(string));
            //s_datatableAnimations.Columns.Add(status);
            // create another column
            DataColumn id = new DataColumn("Id", typeof(string));
            s_datatableAnimations.Columns.Add(id);
            // create one more column
            DataColumn aframes = new DataColumn("AFrames", typeof(System.Int32));
            s_datatableAnimations.Columns.Add(aframes);
            DataColumn desc = new DataColumn("Desc", typeof(string));
            s_datatableAnimations.Columns.Add(desc);


            // create primary key on this field
            DataColumn[] pK = new DataColumn[1];
            pK[0] = id;
            s_datatableAnimations.PrimaryKey = pK;
        }
        public static DataTable GetTableAnimations()
        {
            if (s_datatableAnimations == null)
            {
                InitDataTableAnimations();
            }
            return s_datatableAnimations;
        }

        private static DataTable s_datatableAFrames;
        private static void InitDataTableAFrames()
        {
            s_datatableAFrames = new DataTable("AFrames");

            // create columns for the DataTable
            DataColumn no = new DataColumn("No", typeof(System.Int32));
            s_datatableAFrames.Columns.Add(no);
            //DataColumn status = new DataColumn("Status", typeof(string));
            //s_datatableAFrames.Columns.Add(status);
            // create another column
            DataColumn fid = new DataColumn("FId", typeof(string));
            s_datatableAFrames.Columns.Add(fid);
            DataColumn time = new DataColumn("Time", typeof(System.Int32));
            s_datatableAFrames.Columns.Add(time);
            DataColumn ox = new DataColumn("Ox", typeof(System.Int32));
            s_datatableAFrames.Columns.Add(ox);
            DataColumn oy = new DataColumn("Oy", typeof(System.Int32));
            s_datatableAFrames.Columns.Add(oy);
            DataColumn flag = new DataColumn("Flag", typeof(string));
            s_datatableAFrames.Columns.Add(flag);
        }
        public static DataTable GetTableAFrames()
        {
            if (s_datatableAFrames == null)
            {
                InitDataTableAFrames();
            }
            return s_datatableAFrames;
        }

        public class SRect
        {
            public short X;
            public short Y;
            public short W;
            public short H;
        };
        private static short[] pTmpU = new short[512];
        private static short[] pTmpV = new short[512];
        private static SRect rc = new SRect();
       
        public static SpriteDTO ImportBSprite(string filename)
        {
            SpriteDTO sprite = new SpriteDTO();

            CReadFile pFile = CReadFile.Open(filename);

            int size;

            // version
            ushort nVersion = pFile.ReadU16();


            ushort nFlags = pFile.ReadU16();

            // header
            ushort nModules = pFile.ReadU16();
            ushort nFModules = pFile.ReadU16();
            ushort nFrames = pFile.ReadU16();
            ushort nFrameRects = pFile.ReadU16();
            ushort nAFrames = pFile.ReadU16();
            ushort nAnims = pFile.ReadU16();

            byte[] pModules_Type = new byte[nModules];

            byte[] pModules_Image = new byte[nModules];
            uint[] pModules_Color = new uint[nModules];

            short[] pModules_Width = new short[nModules];
            short[] pModules_Height = new short[nModules];

            int[] pModules_U1 = new int[nModules];
            int[] pModules_V1 = new int[nModules];
            int[] pModules_U2 = new int[nModules];
            int[] pModules_V2 = new int[nModules];

            ushort[] pFModules_Module = new ushort[nFModules];
            byte[] pFModules_Flags = new byte[nFModules];
            short[] pFModules_OffsetX = new short[nFModules];
            short[] pFModules_OffsetY = new short[nFModules];

            ushort[] pFrames_NoFModules = new ushort[nFrames];
            ushort[] pFrames_FM_Start = new ushort[nFrames];
            ushort[] pFrames_FR_Start = new ushort[nFrames];
            ushort[] pFrames_NoFRects = new ushort[nFrames];
            SRect[] pFrames_RC = new SRect[nFrames];
            for (int i = 0; i < nFrames; i++)
                pFrames_RC[i] = new SRect();

            short[] pFrame_RectX = new short[nFrameRects];
            short[] pFrame_RectY = new short[nFrameRects];
            ushort[] pFrame_RectW = new ushort[nFrameRects];
            ushort[] pFrame_RectH = new ushort[nFrameRects];

            ushort[] pAFrames_Frame = new ushort[nAFrames];
            byte[] pAFrames_Time = new byte[nAFrames];
            byte[] pAFrames_Flags = new byte[nAFrames];
            short[] pAFrames_OffsetX = new short[nAFrames];
            short[] pAFrames_OffsetY = new short[nAFrames];

            ushort[] pAnims_NoAFrames = new ushort[nAnims];
            ushort[] pAnims_AF_Start = new ushort[nAnims];


            // load modules..
            pFile.Read(pModules_Type, nModules);
            pFile.Read(pModules_Image, nModules);
            pFile.Read(pModules_Color, nModules * sizeof(uint));

            pFile.Read(pTmpU, nModules * sizeof(short)); //SWAP_SHORT_ARRAY(pTmpU, m_nModules);
            pFile.Read(pTmpV, nModules * sizeof(short)); //SWAP_SHORT_ARRAY(pTmpV, m_nModules);

            for (int i = 0; i < nModules; i++)
            {
                pModules_U1[i] = (int)(pTmpU[i]);
                pModules_V1[i] = (int)(pTmpV[i]);
            }

            pFile.Read(pModules_Width, nModules * sizeof(short));
            pFile.Read(pModules_Height, nModules * sizeof(short));

            // load fmodules..
            pFile.Read(pFModules_Module, nFModules * sizeof(ushort));
            pFile.Read(pFModules_Flags, nFModules * sizeof(byte));
            pFile.Read(pFModules_OffsetX, nFModules * sizeof(short));
            pFile.Read(pFModules_OffsetY, nFModules * sizeof(short));

            // load frames..
            pFile.Read(pFrames_NoFModules, nFrames * sizeof(ushort));
            pFile.Read(pFrames_FM_Start, nFrames * sizeof(ushort));
            pFile.Read(pFrames_NoFRects, nFrames * sizeof(ushort));

            // load frame rects..
            pFile.Read(pFrame_RectX, nFrameRects * sizeof(short));
            pFile.Read(pFrame_RectY, nFrameRects * sizeof(short));
            pFile.Read(pFrame_RectW, nFrameRects * sizeof(ushort));
            pFile.Read(pFrame_RectH, nFrameRects * sizeof(ushort));

            // load aframes..
            pFile.Read(pAFrames_Frame, nAFrames * sizeof(ushort));
            pFile.Read(pAFrames_Time, nAFrames * sizeof(byte));
            pFile.Read(pAFrames_Flags, nAFrames * sizeof(byte));
            pFile.Read(pAFrames_OffsetX, nAFrames * sizeof(short));
            pFile.Read(pAFrames_OffsetY, nAFrames * sizeof(short));

            // load anims..
            pFile.Read(pAnims_NoAFrames, nAnims * sizeof(ushort));
            pFile.Read(pAnims_AF_Start, nAnims * sizeof(ushort));

            //if (nFrames > 0)
            //{
            //    pFrames_FR_Start[0] = 0;
            //    // compute frames rect
            //    for (int i = 0, off = 0; i < nFrames; i++)
            //    {
            //        int minX = (1 << 30);
            //        int minY = (1 << 30);
            //        int maxX = -(1 << 30);
            //        int maxY = -(1 << 30);

            //        for (int j = 0; j < pFrames_NoFModules[i]; j++)
            //        {
            //            GetFModuleRect(ref rc, i, j, 0, 0, 0);

            //            minX = Math.Min(minX, (int)rc.UpperLeftCorner.X);
            //            minY = Math.Min(minY, (int)rc.UpperLeftCorner.Y);
            //            maxX = Math.Max(maxX, (int)rc.LowerRightCorner.X);
            //            maxY = Math.Max(maxY, (int)rc.LowerRightCorner.Y);
            //        }

            //        pFrames_RC[i].X = (short)minX;
            //        pFrames_RC[i].Y = (short)minY;
            //        pFrames_RC[i].W = (short)(maxX - minX);
            //        pFrames_RC[i].H = (short)(maxY - minY);


            //        pFrames_FR_Start[i] = (ushort)off;
            //        off += pFrames_NoFRects[i];

            //    }
            //}
            for(int i =0;i< nModules;i++)
            {
                Module module = new Module();
                module.Id = i;
                module.X = pModules_U1[i];
                module.Y = pModules_V1[i];
                module.W = pModules_Width[i];
                module.H = pModules_Height[i];
                sprite.Modules.Add(module);
            }

            int frameStart = 0;
            for (int i = 0; i < nFrames; i++)
            {
                Frame frame = new Frame();
                frame.Id = i;
                frameStart = pFrames_FM_Start[i];
                
                for (int j = 0; j < pFrames_NoFModules[i]; j++)
                {
                    FModule fmodule = new FModule();
                    fmodule.Mid = pFModules_Module[frameStart + j];
                    fmodule.Ox = pFModules_OffsetX[frameStart + j];
                    fmodule.Oy = pFModules_OffsetY[frameStart + j];
                    fmodule.Flag = pFModules_Flags[frameStart + j];
                    frame.FModules.Add(fmodule);
                }
                sprite.Frames.Add(frame);
            }

            int animStart = 0;
            for (int i = 0; i < nAnims; i++)
            {
                Animation animation = new Animation();
                animation.Id = i;
                animStart = pAnims_AF_Start[i];

                for (int j = 0; j < pAnims_NoAFrames[i]; j++)
                {
                    AFrame aframe = new AFrame();
                    aframe.Fid = pAFrames_Frame[animStart + j];
                    aframe.Time = pAFrames_Time[animStart + j];
                    aframe.Flag = pAFrames_Flags[animStart + j];
                    aframe.Ox = pAFrames_OffsetX[animStart + j];
                    aframe.Oy = pAFrames_OffsetY[animStart + j];
                    animation.AFrames.Add(aframe);
                }
                sprite.Animations.Add(animation);
            }

            return sprite;
        }
        public static SpriteDTO Load(string filename)
        {
            SpriteDTO sprite = new SpriteDTO();
            XmlDocument Doc = new XmlDocument();
            Doc.Load(filename);
            XmlElement root = Doc.DocumentElement;

            //Images
            XmlNodeList imageNodes = root.SelectNodes(@"//IMAGE");
            foreach (XmlNode node in imageNodes)
            {
                ImageInfo imageInfo = new ImageInfo();
                imageInfo.Path = ((XmlElement)node).GetAttribute("path");
                //imageInfo.Transp = int.Parse(((XmlElement)node).GetAttribute("transp"), System.Globalization.NumberStyles.AllowHexSpecifier);
                imageInfo.Transp = XMLHelper.GetAttributeAsHex(node, "transp");                
                try
                {
                    string absDir = Path.Combine(Path.GetDirectoryName(filename), imageInfo.Path);
                    imageInfo.Image = new Bitmap(absDir);
                }
                catch
                {
                    imageInfo.Image = null;
                }
                imageInfo.ImageAttr = new System.Drawing.Imaging.ImageAttributes();
                imageInfo.ImageAttr.SetColorKey(Color.FromArgb(imageInfo.Transp), Color.FromArgb(imageInfo.Transp));
                
                sprite.ImageInfo.Add(imageInfo);
            }
           
            //Module
            XmlNodeList moduleNodes = root.SelectNodes(@"//MODULE");
            foreach (XmlNode node in moduleNodes)
            {
                Module module = new Module();
                module.Id = XMLHelper.GetAttributeAsHex(node, "id");
                module.X = XMLHelper.GetAttributeAsInt(node, "x");
                module.Y = XMLHelper.GetAttributeAsInt(node, "y");
                module.W = XMLHelper.GetAttributeAsInt(node, "w");
                module.H = XMLHelper.GetAttributeAsInt(node, "h");
                module.Desc = ((XmlElement)node).GetAttribute("desc");
                module.Status = EditStatus.Saved;

                sprite.Modules.Add(module);
            }

            //Frame
            XmlNodeList frameNodes = root.SelectNodes(@"//FRAME");
            foreach (XmlNode node in frameNodes)
            {
                Frame frame = new Frame();
                frame.Id = XMLHelper.GetAttributeAsHex(node, "id");
                frame.Desc = ((XmlElement)node).GetAttribute("desc");

                XmlNodeList fmoduleNodes = node.ChildNodes;//node.SelectNodes(@"//FMODULE");
                foreach (XmlNode fmoduleNode in fmoduleNodes)
                {
                    FModule fmodule = new FModule();
                    fmodule.Mid = XMLHelper.GetAttributeAsHex(fmoduleNode, "mid");
                    fmodule.Ox = XMLHelper.GetAttributeAsInt(fmoduleNode, "ox");
                    fmodule.Oy = XMLHelper.GetAttributeAsInt(fmoduleNode, "oy");
                    fmodule.Flag = XMLHelper.GetAttributeAsInt(fmoduleNode, "flags");
                    fmodule.Status = EditStatus.Saved;

                    frame.FModules.Add(fmodule);
                }
                frame.Status = EditStatus.Saved;

                sprite.Frames.Add(frame);
            }

            //Animation
            XmlNodeList animationNodes = root.SelectNodes(@"//ANIMATION");
            foreach (XmlNode node in animationNodes)
            {
                Animation animation = new Animation();
                animation.Id = XMLHelper.GetAttributeAsHex(node, "id");
                animation.Desc = ((XmlElement)node).GetAttribute("desc");

                XmlNodeList aframeNodes = node.ChildNodes;// node.SelectNodes(@"//AFRAME");
                foreach (XmlNode aframeNode in aframeNodes)
                {
                    AFrame aframe = new  AFrame();
                    aframe.Fid = XMLHelper.GetAttributeAsHex(aframeNode, "fid");
                    aframe.Time = XMLHelper.GetAttributeAsInt(aframeNode, "time");
                    aframe.Ox = XMLHelper.GetAttributeAsInt(aframeNode, "ox");
                    aframe.Oy = XMLHelper.GetAttributeAsInt(aframeNode, "oy");
                    aframe.Flag = XMLHelper.GetAttributeAsInt(aframeNode, "flags");
                    aframe.Status = EditStatus.Saved;

                    animation.AFrames.Add(aframe);
                }
                animation.Status = EditStatus.Saved;

                sprite.Animations.Add(animation);
            }

            return sprite;
        }
        public static void Save(SpriteDTO sprite, string filename)
        {            
            XmlDocument Doc = new XmlDocument();

            // Write down the XML declaration
            XmlDeclaration xmlDeclaration = Doc.CreateXmlDeclaration("1.0", "utf-8", null); 


            XmlElement root = Doc.CreateElement("SPRITE");
            Doc.InsertBefore(xmlDeclaration, Doc.DocumentElement);
            Doc.AppendChild(root);

            //Images
             XmlElement imagesNode = Doc.CreateElement("IMAGES");
             root.AppendChild(imagesNode);
            foreach (ImageInfo imageInfo in sprite.ImageInfo)
            {
                XmlElement imageNode = Doc.CreateElement("IMAGE");
                imageNode.SetAttribute("path", imageInfo.Path);
                imageNode.SetAttribute("transp", imageInfo.Transp.ToString("x"));

                imagesNode.AppendChild(imageNode);
            }

            //Module
            XmlElement modulesNode = Doc.CreateElement("MODULES");
            root.AppendChild(modulesNode);
            foreach (Module module in sprite.Modules)
            {
                XmlElement moduleNode = Doc.CreateElement("MODULE");
                moduleNode.SetAttribute("id", module.Id.ToString("x"));
                moduleNode.SetAttribute("x", module.X.ToString());
                moduleNode.SetAttribute("y", module.Y.ToString());
                moduleNode.SetAttribute("w", module.W.ToString());
                moduleNode.SetAttribute("h", module.H.ToString());
                moduleNode.SetAttribute("desc", module.Desc);

                modulesNode.AppendChild(moduleNode);
            }

            //Frame
            XmlElement framesNode = Doc.CreateElement("FRAMES");
            root.AppendChild(framesNode);
            foreach (Frame frame in sprite.Frames)
            {
                XmlElement frameNode = Doc.CreateElement("FRAME");
                frameNode.SetAttribute("id", frame.Id.ToString("x"));
                frameNode.SetAttribute("desc", frame.Desc);

                framesNode.AppendChild(frameNode);

                foreach (FModule fmodule in frame.FModules)
                {
                    XmlElement fmoduleNode = Doc.CreateElement("FMODULE");
                    fmoduleNode.SetAttribute("mid", fmodule.Mid.ToString("x"));
                    fmoduleNode.SetAttribute("ox", fmodule.Ox.ToString());
                    fmoduleNode.SetAttribute("oy", fmodule.Oy.ToString());
                    fmoduleNode.SetAttribute("flags", fmodule.Flag.ToString());

                    frameNode.AppendChild(fmoduleNode);
                }
            }

            //Animation
            XmlElement animationsNode = Doc.CreateElement("ANIMATIONS");
            root.AppendChild(animationsNode);
            foreach (Animation animation in sprite.Animations)
            {
                XmlElement animationNode = Doc.CreateElement("ANIMATION");
                animationNode.SetAttribute("id", animation.Id.ToString("x"));
                animationNode.SetAttribute("desc", animation.Desc);

                animationsNode.AppendChild(animationNode);

                foreach (AFrame aframe in animation.AFrames)
                {
                    XmlElement aframeNode = Doc.CreateElement("AFRAME");
                    aframeNode.SetAttribute("fid", aframe.Fid.ToString("x"));
                    aframeNode.SetAttribute("time", aframe.Time.ToString());
                    aframeNode.SetAttribute("ox", aframe.Ox.ToString());
                    aframeNode.SetAttribute("oy", aframe.Oy.ToString());
                    aframeNode.SetAttribute("flags", aframe.Flag.ToString());

                    animationNode.AppendChild(aframeNode);
                }
            }


            Doc.Save(filename);
        }
    }
}
